/* 观察者模式(使用回调函数实现) */

// https://www.jdon.com/62620

use std::collections::HashMap;

pub trait ProductObservable {
    fn add_product_callback<F: 'static + Fn(&Product)>(&mut self, callback: F);

    fn add_price_callback<F: 'static + Fn(&str, f32)>(&mut self, callback: F);
}

pub struct Product {
    name: String,
    price: f32
}

pub struct Store {
    products: HashMap<String, Product>,
    product_callbacks: Vec<Box<dyn Fn(&Product)>>,
    price_callbacks: Vec<Box<dyn Fn(&str, f32)>>,
}

impl Store {

    fn new() -> Store {
        Store{
            products: HashMap::new(),
            product_callbacks: Vec::new(),
            price_callbacks: Vec::new(),
        }
    }

    fn add_new_product(&mut self, product: Product) {
        let name = product.name.clone();
        self.products.insert(product.name.clone(), product);
        for callback in &self.product_callbacks {
            callback(&self.products[&name]);
        }
    }

    fn set_product_price(&mut self, name: &str, price: f32) {
        let p = self.products.get_mut(name);
        match p {
            Some(product) => {
                product.price = price;
                for callback in &self.price_callbacks {
                    callback(name, price);
                }
            },
            None => println!("Not found")
        }

    }
}

impl ProductObservable for Store {
    fn add_product_callback<F: 'static + Fn(&Product)>(&mut self, callback: F) {
        self.product_callbacks.push(Box::new(callback));
    }

    fn add_price_callback<F: 'static + Fn(&str, f32)>(&mut self, callback: F) {
        self.price_callbacks.push(Box::new(callback));
    }
}

pub struct Customer {
    name: String
}

impl Customer {
    fn on_price_change(&self, product_name: &str, price: f32) {
        println!("客户[{}]收到了商品[{}]的价格变更为[{}]的通知", self.name, product_name, price);
    }
}


#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_observer_1() {
        let product1 = Product {
            name: String::from("苹果"),
            price: 14.0
        };

        let product2 = Product {
            name: String:: from("香蕉"),
            price: 23.0
        };

        let customer = Customer {
            name: String::from("周老板")
        };

        let mut store = Store::new();
        store.add_product_callback(|p| {println!("添加了商品[{}]", p.name);} );
        store.add_price_callback(|name, price| {println!("商品[{}]的价格更改为 [{}]", name, price)});
        store.add_price_callback(move |name, price| {
            customer.on_price_change(name, price);
        });
        store.add_new_product(product1);
        store.add_new_product(product2);

        store.set_product_price("苹果", 15.7);
        store.set_product_price("香蕉", 25.4);
    }
}